﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace MergeShapeFileLib
{
    public class MergeTwoShapeFile
    {
        private FileStream fs1;
        private BinaryReader br1;
        private FileStream fs2;
        private BinaryReader br2;
        private FileStream fs3;
        private BinaryWriter bw;

        private int filecode_1;
        private int unused1_1;
        private int unused2_1;
        private int unused3_1;
        private int unused4_1;
        private int unused5_1;
        private int fileLength_1;
        private int Version_1;
        private int ShapeType_1;
        private double Xmin_1;
        private double Ymin_1;
        private double Xmax_1;
        private double Ymax_1;
        private double Zmin_1;
        private double Zmax_1;
        private double Mmin_1;
        private double Mmax_1;

        private int filecode_2;
        private int unused1_2;
        private int unused2_2;
        private int unused3_2;
        private int unused4_2;
        private int unused5_2;
        private int fileLength_2;
        private int Version_2;
        private int ShapeType_2;
        private double Xmin_2;
        private double Ymin_2;
        private double Xmax_2;
        private double Ymax_2;
        private double Zmin_2;
        private double Zmax_2;
        private double Mmin_2;
        private double Mmax_2;

        private int filecode_3;
        private int unused1_3;
        private int unused2_3;
        private int unused3_3;
        private int unused4_3;
        private int unused5_3;
        private int fileLength_3;
        private int Version_3;
        private int ShapeType_3;

        private double Xmin_3;
        private double Ymin_3;
        private double Xmax_3;
        private double Ymax_3;
        private double Zmin_3;
        private double Zmax_3;
        private double Mmin_3;
        private double Mmax_3;

        public MergeTwoShapeFile()
        {
            //mergeTwoShapeFile(sfile1, sfile2, dfile);
        }

        public void mergeTwoShapeFile(string sfile1, string sfile2, string dfile)
        {
            openAndCreateFile(sfile1,sfile2,dfile);
            readRecordHeader();
            mergeAndWriteHeaderToDfile();
            writeContentsToDfile();
        }

        private void openAndCreateFile(string sfile1, string sfile2, string dfile)
        {
            //读shap文件                      
            //打开文件1，对该文件具有读权限 
            fs1 = new FileStream(sfile1, FileMode.Open, FileAccess.Read);
            //以二进制方式读文件 
            //创建一个二进制数据流读入器，和打开的文件关联 
            br1 = new BinaryReader(fs1);

            fs2 = new FileStream(sfile2, FileMode.Open, FileAccess.Read);
            br2 = new BinaryReader(fs2);

            //以写的方式创建文件3，并将合并的文件头数据写回第三个文件
            fs3 = new FileStream(dfile, FileMode.Create, FileAccess.ReadWrite);
            bw = new BinaryWriter(fs3);
        }

        private void readRecordHeader()
        {
            //读取文件头（前100个字节）            
            filecode_1 = br1.ReadInt32();//便于合并，没有转换
            unused1_1 = br1.ReadInt32();
            unused2_1 = br1.ReadInt32();
            unused3_1 = br1.ReadInt32();
            unused4_1 = br1.ReadInt32();
            unused5_1 = br1.ReadInt32();
            fileLength_1 = ReadUsingBigEndian(br1);//便于计算总长度
            Version_1 = br1.ReadInt32();   //小端模式存的可以直接读
            ShapeType_1 = br1.ReadInt32();
            Xmin_1 = br1.ReadDouble();
            Ymin_1 = br1.ReadDouble();
            Xmax_1 = br1.ReadDouble();
            Ymax_1 = br1.ReadDouble();
            Zmin_1 = br1.ReadDouble();
            Zmax_1 = br1.ReadDouble();
            Mmin_1 = br1.ReadDouble();
            Mmax_1 = br1.ReadDouble();
            //MessageBox.Show(filecode_1.ToString());

            //读取文件头（前100个字节）
            filecode_2 = ReadUsingBigEndian(br2);
            unused1_2 = ReadUsingBigEndian(br2);
            unused2_2 = ReadUsingBigEndian(br2);
            unused3_2 = ReadUsingBigEndian(br2);
            unused4_2 = ReadUsingBigEndian(br2);
            unused5_2 = ReadUsingBigEndian(br2);
            fileLength_2 = ReadUsingBigEndian(br2);
            Version_2 = br2.ReadInt32();   //小端模式存的可以直接读
            ShapeType_2 = br2.ReadInt32();
            Xmin_2 = br2.ReadDouble();
            Ymin_2 = br2.ReadDouble();
            Xmax_2 = br2.ReadDouble();
            Ymax_2 = br2.ReadDouble();
            Zmin_2 = br2.ReadDouble();
            Zmax_2 = br2.ReadDouble();
            Mmin_2 = br2.ReadDouble();
            Mmax_2 = br2.ReadDouble();  
        }

        private void mergeAndWriteHeaderToDfile()
        {
            //合并文件1和文件2的头，形成文件3的头的变量
            filecode_3 = filecode_1;//不变 文件1和文件2中相同的变量值不变 
            unused1_3 = unused1_1;
            unused2_3 = unused2_1;
            unused3_3 = unused3_1;
            unused4_3 = unused4_1;
            unused5_3 = unused5_1;
            fileLength_3 = fileLength_1 + fileLength_2 - 50;// 两个文件长度相加，减去重复的头部大小50个字。因为有100个字节（16位）
            Version_3 = Version_1;//不变 
            ShapeType_3 = ShapeType_1;//不变 
            //比较两个文件中的最小和最大，都取最值
            Xmin_3 = Min(Xmin_1, Xmin_2);
            Ymin_3 = Min(Ymin_1, Ymin_2);
            Xmax_3 = Max(Xmax_1, Xmax_2);
            Ymax_3 = Max(Ymax_1, Ymax_2);
            Zmin_3 = Zmin_1;//不变 
            Zmax_3 = Zmax_1;//不变 
            Mmin_3 = Mmin_1;//不变 
            Mmax_3 = Mmax_1;//不变

            //将文件头写入文件3           
            bw.Write(filecode_3);
            bw.Write(unused1_3);
            bw.Write(unused2_3);
            bw.Write(unused3_3);
            bw.Write(unused4_3);
            bw.Write(unused5_3);
            bw.Write(WriteUsingBigEndian(fileLength_3));
            bw.Write(Version_3);
            bw.Write(ShapeType_3);
            bw.Write(Xmin_3);
            bw.Write(Ymin_3);
            bw.Write(Xmax_3);
            bw.Write(Ymax_3);
            bw.Write(Zmin_3);
            bw.Write(Zmax_3);
            bw.Write(Mmin_3);
            bw.Write(Mmax_3); 
        }

        private void writeContentsToDfile()
        {
            //分别将文件1、2的记录逐条写入文件3。
            //每条记录包含记录头和记录内容。头为8个字节，为4个字，内容长度以字计算，所以记录长度为（4+内容长度）×16位=（4+内容长度）×2 字节
            //超过当前记录长度读取下一个记录
            //如果读取记录时，文件流位置超过文件大小，则停止读取
            //写入文件3时，需要改变的是文件2 记录的编号 必须知道文件1 最后一个编号 将其加上文件2的编号 得到文件三的编号每条记录的总长度=4+记录内容长读*2
            int i = 0;//用来记录文件内容的记录总条数
            int length1 = 100; //记录开始的位置（因为文件头为100字节）

            while (fs3.Position < fileLength_3 * 2)
            {
                while (fs1.Position < fileLength_1 * 2)
                {
                    ++i;
                    //记录头
                    int recordnum = ReadUsingBigEndian(br1);//记录号
                    bw.Write(WriteUsingBigEndian(recordnum));//直接写到文件3

                    int ContentLength = ReadUsingBigEndian(br1);//记录长度
                    bw.Write(WriteUsingBigEndian(ContentLength));//长度写到文件3

                    length1 += (4 + ContentLength) * 2;

                    while (fs1.Position < length1)//一直读记录内容，并写入
                    {
                        bw.Write(br1.ReadInt32());
                    }
                }

                int length2 = 100;
                while (fs2.Position < fileLength_2 * 2)
                {

                    //记录头
                    int recordnum = ReadUsingBigEndian(br2);
                    bw.Write(WriteUsingBigEndian(i + recordnum));//记录号写到文件3,续文件1的记录

                    int ContentLength = ReadUsingBigEndian(br2);//记录长度
                    bw.Write(WriteUsingBigEndian(ContentLength));//长度写到文件3

                    length2 += (4 + ContentLength) * 2;

                    while (fs2.Position < length2)//一直读记录内容，并写入
                    {
                        bw.Write(br2.ReadInt32());
                    }
                }

            }
            fs1.Close();
            br1.Close();
            fs2.Close();
            br2.Close();
            fs3.Close();
            bw.Close();
        }

        private int ReadUsingBigEndian(BinaryReader br)  //因为有大端模式的，所以在读的时候需要转换
        {
            byte[] bytes = br.ReadBytes(4);
            Array.Reverse(bytes);  //调整
            return BitConverter.ToInt32(bytes, 0);

        }

        private int WriteUsingBigEndian(int a)
        {
            byte[] bytes = BitConverter.GetBytes(a);
            Array.Reverse(bytes);
            return BitConverter.ToInt32(bytes, 0);

        }

        private double Max(double a, double b)
        {
            if (a > b)
                return a;
            else 
                return b;
        }

        private double Min(double a, double b)
        {
            if (a < b)
                return a;
            else 
                return b;
        }
    }
}
